use serde::Deserialize;
use std::collections::HashMap;

// Note: JSONSchema7 is represented as serde_json::Value for simplicity.
pub type JsonSchema = serde_json::Value;

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Manifest {
    pub name: String,
    pub description: String,
    #[serde(default)]
    pub readme: Option<String>,
    pub publisher: String,
    pub version: String,
    #[serde(default)]
    pub dependencies: HashMap<String, String>,

    #[serde(default)]
    pub remote_entry: Option<String>,

    pub activation_events: Vec<ActivationEvent>,

    #[serde(default)]
    pub deactivation_events: Vec<ActivationEvent>,

    #[serde(default)]
    pub contributes: Option<ContributeDefinitions>,
    
    #[serde(default)]
    pub contribution_points: Option<ContributionPointDefinitions>,

    #[serde(default)]
    pub contribution_keywords: Vec<ContributionKeywordDefinition>,

    #[serde(default)]
    pub provide_context: bool,

    #[serde(default)]
    pub context_name: Option<String>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum ActivationEvent {
    Simple(String),
    Detailed(ActivationEventStruct),
}

impl ActivationEvent {
    pub fn is_match(&self, event: &str) -> bool {
        match self {
            ActivationEvent::Simple(name) => name == event,
            ActivationEvent::Detailed(s) => s.name == event,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
pub struct ActivationEventStruct {
    pub name: String,
    #[serde(default)]
    pub when: Option<String>,
}

pub type ContributeDefinitions = HashMap<String, Vec<ContributeDefinition>>;

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContributeDefinition {
    #[serde(default)]
    pub cp: Option<String>,
    pub name: String,
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    pub is_replenish: bool,
    #[serde(default)]
    pub replenish_by: Option<String>,
    
    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

pub type ContributionPointDefinitions = HashMap<String, Vec<ContributionPointDefinition>>;

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContributionPointDefinition {
    pub name: String,
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    pub support_dynamics: bool,
    #[serde(default)]
    pub is_replenish: bool,
    #[serde(default)]
    pub replenish_by: Option<String>,

    #[serde(flatten)]
    pub extra: HashMap<String, serde_json::Value>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContributionKeywordDefinition {
    pub name: String,
    #[serde(default)]
    pub support_contribution_point: bool,
    #[serde(default)]
    pub support_dynamics: bool,
    #[serde(default)]
    pub contribute_schema: Option<JsonSchema>,
    #[serde(default)]
    pub contribution_point_schema: Option<JsonSchema>,
    #[serde(default)]
    pub contribute_auto_replenish: Option<AutoReplenish>,
    #[serde(default)]
    pub contribution_point_auto_replenish: Option<AutoReplenish>,
    #[serde(default)]
    pub definition_schema: Option<JsonSchema>,
    #[serde(default)]
    pub disable_name_unique: bool,
    #[serde(default)]
    pub disable_name_obey_namespace_rule: bool,
    #[serde(default)]
    pub provide_context: bool,
    #[serde(default)]
    pub context_name: Option<String>,
    #[serde(default)]
    pub devtools_definition: Option<ContributionKeywordDevtoolDefinition>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AutoReplenish {
    #[serde(default)]
    pub contributes: Option<ContributeDefinitions>,
    #[serde(default)]
    pub contribution_points: Option<ContributionPointDefinitions>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct ContributionKeywordDevtoolDefinition {
    pub typical: String,
}